#betrayed

import bisect
import re
import select
from socket import *
import string
import sys
import time
import types
import threading

VERSION = 0,0,1
DEBUG = 1

class Sender(): 
    source = re.compile(r'([^!]*)!?([^@]*)@?(.*)')
    def __init__(self, nope,source, args): 
        match = Sender.source.match(source or '')
        self.nick, self.user, self.host = match.groups()
        if len(args) > 1: 
            target = args[1]
        else: target = None

_linesep = re.compile(r"\r?\n")

class Protocol(threading.Thread):
    def connect (self, ircserver, port, nicknames, password=None,  username=None,  ircname=None,  locala="",  localp=0, version="crappy irc"):
		#need to check for nick in use error and provide secondary nick or possible random nick 
        if self.connected:
            self.disconnect("Rotate")
        self.data = ""
        self.buffer = ""
        self.server = ircserver
        self.port = port
        self.password = password
        self.username = username
        self.ircname = ircname
        self.localhost = gethostname()
        self.locala = locala
        self.localp = localp
        self.socket = socket(AF_INET,SOCK_STREAM)
        self.alive = True
        self.version = version
        self.support = {}
        try:
            self.socket.connect((self.server, self.port))
        except error:
            self.socket.close()
            self.socket = None
        self.connected = 1
        if self.password:
            self.pass_(self.password)
        
        self.nicknames = nicknames

        self.nick(self.nicknames.pop(0))
        self.user(self.username, self.ircname)
        return self
        
    def __init__(self):
        threading.Thread.__init__(self)
        self.connected = 0
        self.socket = None
        self.alive = False
    def send(self, message):
        if self.socket is None:
            self.disconnect("Connection reset by server")
        try:
            self.socket.send(message + "\r\n")
            if DEBUG:
                print ("FROM LOCAL %s" % (message)) 
        except error,  x:
            self.disconnect("Connection reset by server")
    def pass_(self, password):
        self.send("PASS %s" % password)
    def user(self, realname, username):
        self.send("USER %s 0 * :%s" % (username, realname))
    def nick(self, nickname):
        self.nickname = nickname
        self.send("NICK %s" % nickname)
    def nextnick(self):
        if len(self.nicknames) > 0:
            self.nick(self.nicknames.pop(0))
        else:
            self.disconnect("")
    def quit(self, message):
        self.send("QUIT%s" % (message and (" :%s" % message)))
    def disconnect(self, message):
        if not self.connected:
            return
        self.connected = 0
        self.quit(message)
        try:
            self.socket.close()
        except socket.error,  x:
            pass
        self.socket=None
        self.alive = False

    def recv(self):
        try:
		    data=self.socket.recv(2**14)
        except:
            self.disconnect("Server reset by 1")
            return
        if not data:
            self.disconnect("Server reset by 2")
            return
        lines = _linesep.split(self.data+data)
        #print lines
        self.data = lines[-1]
        lines = lines[:-1]
        for line in lines:
            if DEBUG:
                print "FROM SERVER: %s" % line
            if not line: 
                continue
            self.process_data(line)
        return line

    def process_data(self,data):
        line = data

        if line is None:
            return
        if line.startswith(':'): 
            source, line = line[1:].split(' ', 1)
        else: source = None
        if ' :' in line: 
            argstr, text = line.split(' :', 1)
        else: argstr, text = line, ''
        args = argstr.split()
        sender = Sender(self, source, args)
        if args[0] == "PING": 
            self.send ("PONG %s" % text)
        #print tuple([text] + args)
        self.dispatch(sender, tuple([text] + args))

    def dispatch(self, sender, args):
        event = args[1]
        if event == "433":
            self.nextnick()
        elif event == "001":
            if DEBUG:
                print "!!! 001 handled -- connection authorized"

            channels = []
            keys = []
            for ch in self.channels:
                try:
                    """channels with keys need to come first"""
                    chan, key = ch.split(':')
                    channels.insert(0, chan)
                    keys.insert(0, key)
                except:
                    chan = ch
                    channels.append(chan)

            self.joinc(",".join(channels), ",".join(keys))
        elif event == "005":
            """If we follow the protocol, then we are getting notice that we
            need to connect to a different server. If we follow convention, we
            are getting server info."""
            self.parse005(args)

        elif event == "NICK":
            if sender.nick == self.nick:
                self.nick = args[0]
    def get_args(self,data):
        return data.split()[4:]
    def get_args1(self,data):
        return data.split()[3:]
    def get_username(self,data):
        return data.split("!")[0].strip(":")
    def get_hostname(self,data):
        return data.split("!")[1].split(" ")[0]
    def run(self):
        while self.alive:
            data = self.recv()
            #self.process_data(data)

    def is_channel(self, string):
        return string and string[0] in self.support['chantypes']

    def parse005(self, args):
        data = args[3:]

        for item in data:
            if "=" in item:
                key, values = item.split("=")
                self.support[key.lower()] = values
            else:
                self.support[item.lower()] = True

    """Yar, here be output methods"""
    def joinc(self, channel,key=""):
        #channel and key match string set up as "channel, channel1" and "channel,channel1"
        channels = channel.replace(" ","")
        keys = key.replace(" ","")
        self.send("JOIN %s%s" % (channels, keys and (" " + keys) or ""))
    def privmsg(self, who, what):
        self.send("PRIVMSG %s :%s" % (who, what))
    def kick(self, channel, nick, reason="kicked"):
        self.send("KICK %s %s :%s" % (channel, nick, reason))
    def action(self, target, action):
        self.ctcp("ACTION", target, action)
    def ctcp(self, ctcptype, target, parameter=""):
        ctcptype = ctcptype.upper()
        self.privmsg(target, "\001%s%s\001" % (ctcptype, parameter and (" " + parameter) or ""))
    def notice(self, target, text):
        self.send("NOTICE %s :%s" % (target, text))	
    def whois(self, target):
        self.send("WHOIS ,%s" % (target))
    def mode(self, target, mode, params=None):
        self.send("MODE %s %s %s" % (target, mode, params))
    def usermode(self, target, mode):
        self.send("MODE %s %s" % (target, mode))
    def ctcp_reply(self, who, what):
        self.notice(who, "\001%s\001" % what)
    def topic(self, channel, topic=None):
        self.send("TOPIC %s%s" % (channel, topic  and (" :%s" % topic)))
    def operator(self, user, password):
        self.send("OPER %s %s" % (user, password))
    def names(self,channel=None):
        #expects a list of channels ["#channel1", "channel2"]
        self.send("NAMES%s" % (channel and( " " + ",".join(channel) or "")))
    def list(self, channel=None, target=""):
        #expects a list of channel
        self.send("LIST%s%s" % (channel and (" " + ",".join(channel)) or "", target and(" " + target)))
    def part(self,channel,message=None):
        channels = channel.replace(" ","")
        self.send("PART %s%s" % (channels, message and (" :%s" % message)))

    def modes(self,data):
        count = 0
        type = ""
        rdata = []
        dsplit = data.split()
        try:
            bdata, edata = dsplit[0], dsplit[1:]
        except:
            return None
        if bdata[0] not in "+-": return []
        for i in bdata:
            if i in "-+":
                type = i
            elif i in "klvo":
                if len(edata) >= count + 1:
                    rdata.append ([type, i, edata[count]])
                    count = count +1
                else: 
                    rdata.append([type, i, None])
            else:
                rdata.append([type, i, None])
        return rdata



